import java.lang.NumberFormatException;

/**
 * Wrapper class for strings in the Graphr Language
 * @author Paul Dix
 */
public class GraphrString extends GraphrDataType {
	//Variable Declaration
	private String string;
	
	/**
	 * Constructs a newly allocated GraphrString object that represents the string argument.
	 * @param val - the string to be represented by the GraphrString.
	 */
	public GraphrString(String val) {
	    this.string = val;
	  }
	
	/**
	 * Returns the string value of this GraphrString object.
	 * @returns the string value of this GraphrString object.
	 */
	public String getString() {
	    return this.string;
	  }
	
	/**
	 * Returns a string representation of the object.
	 * @returns a string representation of the object.
	 */
	public String toString() {
	    return this.string;
	}
	
	/**
	 * Overloaded add method from GraphrDataType. Returns a new GraphrDataType representing this GraphrString plus rVal.<br>
	 * Will return a GraphrNumber if rVal is a GraphrNumber and this GraphrString can be converted into a number. 
	 * Otherwise will return a new GraphrString containing the concatenation of this GraphrString and rVal.
	 * @param rVal - the GraphrDataType to add.
	 * @returns the result of adding this GraphrString to rVal.
	 */
	public GraphrDataType add(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrString(this.toString() + rVal.toString());        
			}
			return new GraphrNumber(l + r);
		} catch (NumberFormatException ex) {
			return new GraphrString(this.toString() + rVal.toString());      
		}
	}
	
	/**
	 * Overloaded subtract method from GraphrDataType. Returns a new GraphrDataType representing this GraphrString plus rVal.<br>
	 * Will return a GraphrNumber if rVal is a GraphrNumber and this GraphrString can be converted into a number. 
	 * Otherwise will return a new GraphrString containing the concatenation of this GraphrString and rVal.
	 * @param rVal - the GraphrDataType to subtract.
	 * @returns the result of subtracting rVal from this GraphrString.
	 */
	public GraphrDataType subtract(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrString(this.toString() + rVal.toString());        
			}
			return new GraphrNumber(l - r);
		} catch (NumberFormatException ex) {
			return new GraphrString(this.toString() + rVal.toString());      
		}
	}
	
	/**
	 * Overloaded multiply method from GraphrDataType. Returns a new GraphrDataType representing this GraphrString plus rVal.<br>
	 * Will return a GraphrNumber if rVal is a GraphrNumber and this GraphrString can be converted into a number. 
	 * Otherwise will return a new GraphrString containing the concatenation of this GraphrString and rVal.
	 * @param rVal - the GraphrDataType to multiply.
	 * @returns the result of multiplying this GraphrString to rVal.
	 */
	public GraphrDataType multiply(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrString(this.toString() + rVal.toString());        
			}
			return new GraphrNumber(l * r);
		} catch (NumberFormatException ex) {
			return new GraphrString(this.toString() + rVal.toString());      
		}
	}
	
	/**
	 * Overloaded divide method from GraphrDataType. Returns a new GraphrDataType representing this GraphrString plus rVal.<br>
	 * Will return a GraphrNumber if rVal is a GraphrNumber and this GraphrString can be converted into a number. 
	 * Otherwise will return a new GraphrString containing the concatenation of this GraphrString and rVal.
	 * @param rVal - the GraphrDataType to divide.
	 * @returns the result of dividing this GraphrString by rVal.
	 */
	public GraphrDataType divide(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrString(this.toString() + rVal.toString());        
			}
			return new GraphrNumber(l / r);
		} catch (NumberFormatException ex) {
			return new GraphrString(this.toString() + rVal.toString());      
		}
	}
	
	/**
	 * Returns a GraphrDataType representing the mod of the left and right values.
	 * @param rVal - the GraphrDataType to mod with.
	 * @returns GraphrDataType if both operands can be parsed into numbers, a mod operation is performed, otherwise the left hand operand is returned.
	 */
	public GraphrDataType mod(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return this;
			}
			return new GraphrNumber(l % r);
		} catch (NumberFormatException ex) {
			return this;
		}	  
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString equals rVal.
	 * @param rVal - the GraphrString to compare with.
	 * @returns <b>true</b> if this GraphrString equals rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean equals(GraphrString rVal) {
		return new GraphrBoolean(this.string.equals(rVal.getString()));
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString equals rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrString equals rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean equals(GraphrDataType rVal) {
	  if (rVal.getClass() == this.getClass()) {
		  return new GraphrBoolean(this.string.equals(((GraphrString)rVal).getString()));
	  }
	  else {
	    return new GraphrBoolean(false);
	  }
	}

	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString is less than rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrString is less than rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean lessThan(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrBoolean(false);
			}
			return new GraphrBoolean(l < r);
		} catch (NumberFormatException ex) {
			if (rVal.getClass() == GraphrString.class) {
			  int compare = this.string.compareTo(((GraphrString)rVal).getString());
			  if (compare >= 0) {
			    return new GraphrBoolean(false);
			  }
			  else {
			    return new GraphrBoolean(true);
			  }
			}
			else {
			  return new GraphrBoolean(false);
			}
		}
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString is greater than rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrString is greater than rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean greaterThan(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrBoolean(false);
			}
			return new GraphrBoolean(l > r);
		} catch (NumberFormatException ex) {
			if (rVal.getClass() == GraphrString.class) {
			  int compare = this.string.compareTo(((GraphrString)rVal).getString());
			  if (compare <= 0) {
			    return new GraphrBoolean(false);
			  }
			  else {
			    return new GraphrBoolean(true);
			  }
			}
			else {
			  return new GraphrBoolean(false);
			}
		}
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString is less than or equal to rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrString is less than or equal to rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean lessThanEqualTo(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrBoolean(false);
			}
			return new GraphrBoolean(l <= r);
		} catch (NumberFormatException ex) {
			if (rVal.getClass() == GraphrString.class) {
			  int compare = this.string.compareTo(((GraphrString)rVal).getString());
			  if (compare > 0) {
			    return new GraphrBoolean(false);
			  }
			  else {
			    return new GraphrBoolean(true);
			  }
			}
			else {
			  return new GraphrBoolean(false);
			}
		}
	}
	
	/**
	 * Returns a new GraphrBoolean representing whether this GraphrString is greater than or equal to rVal.
	 * @param rVal - the GraphrDataType to compare with.
	 * @returns <b>true</b> if this GraphrString is greater than or equal to rVal; <b>false</b> otherwise.
	 */
	public GraphrBoolean greaterThanEqualTo(GraphrDataType rVal) {
		try {
			Double l = Double.parseDouble(this.string);
			Double r = null;
			if (this.getClass() == rVal.getClass()) {
				r = Double.parseDouble(((GraphrString)rVal).getString());
			}
			else if (rVal.getClass() == GraphrNumber.class) {
				r = new Double(((GraphrNumber)rVal).getNumber().doubleValue());
			}
			else {
				return new GraphrBoolean(false);
			}
			return new GraphrBoolean(l >= r);
		} catch (NumberFormatException ex) {
			if (rVal.getClass() == GraphrString.class) {
			  int compare = this.string.compareTo(((GraphrString)rVal).getString());
			  if (compare < 0) {
			    return new GraphrBoolean(false);
			  }
			  else {
			    return new GraphrBoolean(true);
			  }
			}
			else {
			  return new GraphrBoolean(false);
			}
		}
	}
}